package com.joysuch.wwyt.patrol.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.base.Splitter;
import com.joysuch.wwyt.common.constant.Constant;
import com.joysuch.wwyt.core.entity.BaseUser;
import com.joysuch.wwyt.core.enums.DeleteFlag;
import com.joysuch.wwyt.core.repository.BaseUserDao;
import com.joysuch.wwyt.core.support.Context;
import com.joysuch.wwyt.mh.entity.MhMonitoringType;
import com.joysuch.wwyt.mh.repository.MhMonitoringTypeDao;
import com.joysuch.wwyt.patrol.constant.CheckResultStatus;
import com.joysuch.wwyt.patrol.entity.PatrolCheckItem;
import com.joysuch.wwyt.patrol.entity.PatrolCheckPlan;
import com.joysuch.wwyt.patrol.entity.PatrolTask;
import com.joysuch.wwyt.patrol.entity.bean.*;
import com.joysuch.wwyt.patrol.entity.dto.*;
import com.joysuch.wwyt.patrol.entity.vo.PatrolCheckReportVo;
import com.joysuch.wwyt.patrol.entity.vo.PatrolDailyReportVo;
import com.joysuch.wwyt.patrol.entity.vo.StatTrendBean;
import com.joysuch.wwyt.patrol.entity.vo.StatTrendListBean;
import com.joysuch.wwyt.patrol.enums.DataTypeEnum;
import com.joysuch.wwyt.patrol.mapper.PatrolCheckPlanMapper;
import com.joysuch.wwyt.patrol.mapper.PatrolRecordItemMapper;
import com.joysuch.wwyt.patrol.service.*;
import com.joysuch.wwyt.patrol.utils.StatisticsTableTitleUtil;
import com.joysuch.wwyt.util.FileUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.util.*;
import java.util.stream.Collectors;


@Service
@Slf4j
public class PatrolStatisticsServiceImpl implements PatrolStatisticsService {

    @Autowired
    private PatrolTaskService patrolTaskService;
    @Autowired
    private PatrolRecordItemService patrolRecordItemService;
    @Autowired
    private PatrolRecordService recordService;
    @Autowired
    private PatrolRecordItemMapper recordItemMapper;
    @Autowired
    private PatrolCheckItemService checkItemService;
    @Autowired
    private PatrolCheckPlanMapper patrolCheckPlanMapper;
    @Autowired
    private BaseUserDao baseUserDao;
    @Autowired
    private MhMonitoringTypeDao monitoringTypeDao;

    @Override
    public PatrolDailyReportVo dailyReportPage(PatrolDailySearchBean searchBean) {
        Page<PatrolDailyReportDto> page = new Page<>(searchBean.getPage() + 1, searchBean.getSize());
        PatrolDailyReportVo reportVo = PatrolDailyReportVo.builder().build();
        switch (searchBean.getStatType()) {
            case USER:
                Page<PatrolDailyReportDto> userPage = patrolCheckPlanMapper.statisticsListByUser(page, searchBean, Context.getCurrentTenantId(), Context.getCurrentOrgCode());
                PatrolDailyReportDto userStat = patrolCheckPlanMapper.queryStatisticsByUser(searchBean, Context.getCurrentTenantId(), Context.getCurrentOrgCode());
                reportVo = PatrolDailyReportVo.builder().stat(userStat).statList(userPage.getRecords()).length(userPage.getTotal()).build();
                break;
            case PLAN:
                Page<PatrolDailyReportDto> planPage = patrolCheckPlanMapper.statisticsListByPlan(page, searchBean, Context.getCurrentTenantId(), Context.getCurrentOrgCode());
                PatrolDailyReportDto patrolStat = patrolCheckPlanMapper.queryStatisticsByPlan(searchBean, Context.getCurrentTenantId(), Context.getCurrentOrgCode());
                reportVo = PatrolDailyReportVo.builder().stat(patrolStat).statList(planPage.getRecords()).length(planPage.getTotal()).build();
                break;

        }
        return reportVo;
    }


    @Override
    public void exportDailyReport(PatrolDailySearchBean searchBean, HttpServletRequest request, HttpServletResponse
            response) throws IOException {
        switch (searchBean.getStatType()) {
            case USER:
                List<PatrolDailyReportDto> statListUser = patrolCheckPlanMapper.statisticsListByUser(searchBean, Context.getCurrentTenantId(), Context.getCurrentOrgCode());
                List<PatrolDailyReportUserBean> userList = new ArrayList<>();
                for (PatrolDailyReportDto each : statListUser) {
                    PatrolDailyReportUserBean statUser = new PatrolDailyReportUserBean();
                    BeanUtils.copyProperties(each, statUser);
                    userList.add(statUser);
                }
                OutputStream userStatOut = response.getOutputStream();
                ExcelWriter userStatWriter = ExcelUtil.getWriter(true);
                buildUserExcelHeader(userStatWriter);
                userStatWriter.write(userList, true);
                String userFileName = FileUtil.getEncodeFileName(request, "巡检日报-人员");
                response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
                response.setHeader("Content-Disposition", String.format("attachment;filename=%s", userFileName + ".xlsx"));
                userStatWriter.flush(userStatOut, true);
                userStatWriter.close();
                IoUtil.close(userStatOut);
                break;
            case PLAN:
                List<PatrolDailyReportDto> statListPlan = patrolCheckPlanMapper.statisticsListByPlan(searchBean, Context.getCurrentTenantId(), Context.getCurrentOrgCode());
                List<PatrolDailyReportPlanBean> planList = new ArrayList<>();
                for (PatrolDailyReportDto each : statListPlan) {
                    PatrolDailyReportPlanBean statPlan = new PatrolDailyReportPlanBean();
                    BeanUtils.copyProperties(each, statPlan);
                    planList.add(statPlan);
                }
                OutputStream planStatOut = response.getOutputStream();
                ExcelWriter planStatWriter = ExcelUtil.getWriter(true);
                buildPlanExcelHeader(planStatWriter);
                planStatWriter.write(planList, true);
                String planFileName = FileUtil.getEncodeFileName(request, "巡检日报-路线");
                response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
                response.setHeader("Content-Disposition", String.format("attachment;filename=%s", planFileName + ".xlsx"));
                planStatWriter.flush(planStatOut, true);
                planStatWriter.close();
                IoUtil.close(planStatOut);
                break;
        }
    }

    @Override
    public Page<PatrolCheckReportDto> checkReportPlanStat(PatrolCheckSearchBean searchBean) {
        Page<PatrolCheckReportDto> page = new Page<>(searchBean.getPage() + 1, searchBean.getSize());
        Page<PatrolCheckReportDto> checkReports = patrolCheckPlanMapper.checkReportPlanStat(page, searchBean, Context.getCurrentTenantId(), Context.getCurrentOrgCode());
        List<PatrolCheckReportDto> records = checkReports.getRecords();
        if (CollectionUtil.isNotEmpty(records)) {
            records.forEach(each -> {
                //检查人员
                String checkUserIds = each.getCheckUserIds();
                if (StringUtils.isNotBlank(checkUserIds)) {
                    List<Long> ids = Splitter.on(Constant.SPLIT_COMMA).trimResults().splitToList(checkUserIds).stream().map(Long::new).collect(Collectors.toList());
                    List<String> userNames = baseUserDao.getRealNameByIds(ids);
                    each.setCheckUserIds(String.join(Constant.SPLIT_COMMA, userNames));
                }
            });
        }
        return checkReports;
    }

    @Override
    public PatrolCheckReportVo planDetail(StatPlanTitleSearchBean searchBean) {
        Long taskId = patrolTaskService.findLastTaskId(searchBean.getPlanId());
        Page<TaskRecordItemDto> recordItemList = patrolRecordItemService.getLastTaskRecordItemByTaskId(searchBean.getPage(), searchBean.getSize(), taskId, searchBean.getPointName(), searchBean.getDeviceNameOrCode());
        List<Long> checkProjectId = recordItemList.getRecords().stream().map(TaskRecordItemDto::getCheckProjectId).collect(Collectors.toList());
        LambdaQueryWrapper<PatrolTask> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(PatrolTask::getDeleteFlag, "0");
        wrapper.eq(PatrolTask::getPlanId, searchBean.getPlanId());
        wrapper.eq(PatrolTask::getStatus, "已完成");
        wrapper.orderBy(true, true, PatrolTask::getStartTime);
        if (searchBean.getStartTime() != null) {
            searchBean.setEndTime(DateUtil.endOfMonth(searchBean.getStartTime()));
            wrapper.ge(PatrolTask::getStartTime,searchBean.getStartTime());
            wrapper.le(PatrolTask::getStartTime,searchBean.getEndTime());
        }
        List<PatrolTask> taskList = patrolTaskService.list(wrapper);
        List<PatrolPlanCycleDto> planCycles = new ArrayList<>();
        PatrolCheckPlan plan = patrolCheckPlanMapper.selectById(searchBean.getPlanId());
        List<Long> taskIds =new ArrayList<>();
        taskList.forEach(task -> {
            PatrolPlanCycleDto cycle = new PatrolPlanCycleDto();
            cycle.setTaskId(task.getId());
            cycle.setStartTime(task.getStartTime());
            cycle.setEndTime(task.getEndTime());
            taskIds.add(task.getId());
            planCycles.add(cycle);
        });
        StatisticsTableTitleUtil.convertTableTitle(planCycles, plan);
        // 没有任务或没有检查项目，均返回空数据
        List<ItemCheckResultDto> checkResultList = CollectionUtil.isNotEmpty(taskIds) && CollectionUtil.isNotEmpty(checkProjectId)
                ? patrolRecordItemService.getItemCheckResultByTaskAndCkProject(taskIds,checkProjectId) : new ArrayList<>();
        return PatrolCheckReportVo.builder().recordItemList(recordItemList).planCycles(planCycles).checkResultList(checkResultList).build();
    }


    @Override
    public Page<StatPlanTitleDto> getPlanTitle(StatPlanTitleSearchBean searchBean) {
        if (searchBean.getStartTime() != null) {
            searchBean.setEndTime(DateUtil.endOfMonth(searchBean.getStartTime()));
        }
        Page<StatPlanTitleDto> page = new Page<>(searchBean.getPage() + 1, searchBean.getSize());
        Page<StatPlanTitleDto> planTitle = patrolCheckPlanMapper.pagePlanTitle(page, searchBean);
        List<StatPlanTitleDto> planTitleRecords = planTitle.getRecords();

        if (CollectionUtil.isEmpty(planTitleRecords)) {
            return planTitle;
        }

        // 获取计划id —> 分配人的ids 映射
        Map<Long, List<Long>> assignMapper = planTitle.getRecords().stream().collect(Collectors.toMap(StatPlanTitleDto::getId, r -> {
            String assignUserIdsStr = r.getAssignUserIds();
            return Splitter.on(Constant.SPLIT_COMMA).trimResults().splitToList(assignUserIdsStr).stream().map(Long::new).collect(Collectors.toList());
        }));
        // 获取计划中的所有用户id
        Set<Long> allAssignIds = new HashSet<>();
        assignMapper.entrySet().stream().forEach(each -> {
            allAssignIds.addAll(each.getValue());
        });

        List<BaseUser> users = baseUserDao.getAllByIds(allAssignIds);
        Map<Long, BaseUser> userMapper = users.stream().collect(Collectors.toMap(BaseUser::getId, r -> r));

        planTitle.getRecords().forEach(each -> {
            List<Long> eachAssignIds = assignMapper.get(each.getId());
            String assignNames = eachAssignIds.stream().map(userMapper::get).filter(e -> DeleteFlag.EFFECTIVE.getCode().equals(e.getDeleteFlag()) && !Integer.valueOf(44).equals(e.getState())).map(BaseUser::getRealName).collect(Collectors.joining(Constant.SPLIT_COMMA));
            each.setAssignUser(assignNames);
        });

        // 漏检数，未到位数
        List<Long> planIds = planTitleRecords.stream().map(StatPlanTitleDto::getId).collect(Collectors.toList());
        List<SimpleRecordResultDto> simpleCheckResults = recordService.getSimpleRecordsByPlanIds(planIds, searchBean);
        Map<Long, List<SimpleRecordResultDto>> planResultMapping = simpleCheckResults.stream().collect(Collectors.groupingBy(SimpleRecordResultDto::getPlanId));
        planTitleRecords.forEach(each -> {
            List<SimpleRecordResultDto> resultList = planResultMapping.get(each.getId());
            if (CollectionUtil.isNotEmpty(resultList)) {
                // 漏检
                long unCheckCount = resultList.stream().filter(e -> CheckResultStatus.UNCHECK.equals(e.getCheckResult())).count();
                // 签到异常
                long unArriveCount = resultList.stream().filter(e -> CheckResultStatus.ABNORMAL.equals(e.getVerifyState())).count();
                each.setUnCheckCount(unCheckCount).setUnArriveCount(unArriveCount);
            }
        });

        return planTitle;
    }

    @Override
    public StatTrendBean getTrend(StatTrendSearchBean searchBean) {
        String resultField = getResultField(searchBean.getResultType());
        Long projectId = searchBean.getProjectId();
        StatTrendBean statTrend = new StatTrendBean();
        PatrolCheckItem checkItem = checkItemService.getById(projectId);
        statTrend.setCheckStandard(checkItem.getCheckStandard())
                .setName(checkItem.getCheckItem())
                .setNormalLeft(checkItem.getNormalLeft())
                .setNormalRight(checkItem.getNormalRight())
                .setSymbol(checkItem.getSymbol());
        if (DataTypeEnum.NUM.getValue().equals(checkItem.getDataType()) && checkItem.getDataTypeId() != null) {
            MhMonitoringType monitoringType = monitoringTypeDao.getOne(checkItem.getDataTypeId());
            statTrend.setUnit(monitoringType != null ? monitoringType.getUnit() : "");
        }
        List<StatTrendListBean> trendList = recordItemMapper.listCheckResultByTime(projectId,
                searchBean.getStartTime(),
                DateUtil.endOfDay(searchBean.getEndTime()),
                resultField,
                searchBean.getPlanId());
        convertTrendNumValue(trendList);
        statTrend.setTrendList(trendList);
        return statTrend;
    }

    void convertTrendNumValue(List<StatTrendListBean> trendList) {
        // 2.10版本之前产生的数据带单位，如：12℃，解析异常直接忽略
        if (CollectionUtil.isNotEmpty(trendList)) {
            trendList.forEach(each -> {
                if (StringUtils.isNotBlank(each.getValue())) {
                    try{
                        each.setNumValue(Double.parseDouble(each.getValue()));
                    }catch (Exception e) {
                        log.error("value parse error:{}", each.getValue());
                    }
                }
            });
        }
    }

    private String getResultField(Integer resultType) {
        switch (resultType) {
            case 0:
                return "check_result";
            case 1:
                return "check_result1";
            case 2:
                return "check_result2";
            default:
                throw new IllegalArgumentException(String.format("not support param resultType:%s", resultType));
        }
    }

    private void buildPlanExcelHeader(ExcelWriter writer) {
        writer.addHeaderAlias("name", "巡检路线");
        writer.addHeaderAlias("departName", "责任部门");
        writer.addHeaderAlias("total", "任务数量");
        writer.addHeaderAlias("done", "完成数量");
        writer.addHeaderAlias("missed", "漏检点位数量");
        writer.addHeaderAlias("notInPosition", "未到位检查点数量");
        writer.addHeaderAlias("exceptCount", "异常隐患数量");
        writer.addHeaderAlias("processed", "整改完成数量");
    }

    private void buildUserExcelHeader(ExcelWriter writer) {
        writer.addHeaderAlias("name", "巡检人员");
        writer.addHeaderAlias("departName", "所在部门");
        writer.addHeaderAlias("total", "任务数量");
        writer.addHeaderAlias("done", "完成数量");
        writer.addHeaderAlias("missed", "漏检点位数量");
        writer.addHeaderAlias("notInPosition", "未到位检查点数量");
        writer.addHeaderAlias("exceptCount", "异常隐患数量");
    }

}
